﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using LinqToAmazonSL;
using SilverlightApplication.LanguageResources;

namespace SilverlightApplication.UIControls
{
  
    public partial class SearchController : UserControl
    {
        //current item (DVD or BOOK)
        public AmazonItem item { get; set; }
                
        Carousel.Carousel carousel;
        public MagnifierControl MagnifierCtrl { get; set; }

        IQueryable<AmazonBook> QueryBook = null;
        IQueryable<AmazonDVD> QueryDvd = null;

        const int RESULTS_PER_PAGE = 10;
        const int MAX_PAGES = 500;

        public delegate void FirstPageArrivedEventHandler();
        public event FirstPageArrivedEventHandler firstPageArrivedEventHandler;

        int PageLoading;
        int LastPageGot;
        int ResultCount;
        int CurrentSearch;

        PageState [] Pages;

        public Carousel.Carousel Carousel
        {
            get { return carousel; }
            set
            {
                carousel = value;
                carousel.displayingItemChanged += new SilverlightApplication.UIControls.Carousel.Carousel.DisplayingItemChanged(carousel_displayingItemChanged);
            }
        }

        
        int nextPageToLoad()
        {
            return (carousel.DisplayingItemResultNumber - 1) / RESULTS_PER_PAGE + 2;
        }


        void carousel_displayingItemChanged()
        {
            updatePagingMonitor();

            int nextPage = nextPageToLoad();
            int totalPagesCount = (ResultCount != -1) ? ResultCount / RESULTS_PER_PAGE + 1 : -1;

            // If ResultCount hasn't arrived yet, we don't ask for the next page

            if (nextPage <= Math.Min(totalPagesCount, MAX_PAGES) && !GettingPage && Pages[nextPage] == PageState.NotLoaded)
            {
                item.Page = nextPage;
                GetPage(item); 
            }
        }


        void updatePagingMonitor()
        {
            string resultCount = ResultCount.ToString();

            if (ResultCount == -1)
                resultCount = "?";

            if (carousel.DisplayableItems.Count == 0)
            {
                MagnifierCtrl.Visibility = Visibility.Visible;
                MagnifierCtrl.Play();
                tblInfo.Text = LanguageLoader.GetString("noSearchresults");//"No Items Found";
            }
            else
            {
                tblInfo.Text = carousel.DisplayingItemResultNumber
                    + " / " + resultCount;
            }
        }


        public SearchController()
        {
            InitializeComponent();

            Pages = new PageState[MAX_PAGES + 1];
            CurrentSearch = 0;
        }

        
        public void ClearSearch()
        {
            for (int i = 1; i <= MAX_PAGES; i++)
            {
                Pages[i] = PageState.NotLoaded;
            }

            carousel.Clear();

            LastPageGot = 0;
            ResultCount = -1;
            PageLoading = 0;

            tblInfo.Text = "";
        }


        public void DoSearch(SearchCriteria criteria)
        {
            // If it's not the first search, we clean the previous one
            if (CurrentSearch != 0)
            {
                ClearSearch();
            }

            CurrentSearch++;

            if (criteria.Item.GetType() == typeof(AmazonBook))
            {
                Type t = criteria.Item.GetType();
                AmazonBook b = criteria.Item as AmazonBook;

                AmazonQueryProvider<AmazonBook> provider = new AmazonQueryProvider<AmazonBook>(new AmazonBookQueryCriteria(), StatusBar.amazonKey);
                Query<AmazonBook> queryable = new Query<AmazonBook>(provider);
                IQueryable<AmazonBook> query = null;

                query = from book in queryable
                        where book.Title == b.Title && book.Author == b.Author &&
                              book.Publisher == b.Publisher &&
                              book.MaximumPrice == b.MaximumPrice && book.MinimumPrice == b.MinimumPrice &&
                              book.Keywords == b.Keywords
                        select book;

                if (criteria.OrderBy == OrderClass.Title)
                {
                    query = criteria.Direction == OrderDirection.Ascending ? query.OrderBy(book => book.Title) : query.OrderByDescending(book => book.Title);
                }
                else if (criteria.OrderBy == OrderClass.Price)
                {
                    query = criteria.Direction == OrderDirection.Ascending ? query.OrderBy(book => book.Price) : query.OrderByDescending(book => book.Price);
                }

                this.QueryBook = query;
                item = b;
                item.Page = 1;
                GetResultCount(criteria);
                GetPage(b);
            }
            else if (criteria.Item.GetType() == typeof(AmazonDVD))
            {
                AmazonDVD d = criteria.Item as AmazonDVD;

                AmazonQueryProvider<AmazonDVD> provider = new AmazonQueryProvider<AmazonDVD>(new AmazonDVDQueryCriteria(), StatusBar.amazonKey);
                Query<AmazonDVD> queryable = new Query<AmazonDVD>(provider);
                IQueryable<AmazonDVD> query = null;

                query = from dvd in queryable
                        where dvd.Title == d.Title && dvd.Actor == d.Actor &&
                              dvd.Publisher == d.Publisher &&
                              dvd.Director == d.Director &&
                              dvd.MaximumPrice == d.MaximumPrice && dvd.MinimumPrice == d.MinimumPrice &&
                              dvd.Keywords == d.Keywords
                        select dvd;

                if (criteria.OrderBy == OrderClass.Title)
                {
                    query = query.OrderBy(dvd => dvd.Title);
                }
                else if (criteria.OrderBy == OrderClass.Price)
                {
                    query = criteria.Direction == OrderDirection.Ascending ? query.OrderBy(dvd => dvd.Price) : query.OrderByDescending(dvd => dvd.Price);
                }

                this.QueryDvd = query;
                item = d;
                item.Page = 1;
                GetResultCount(criteria);
                GetPage(d);
            }
        }


        void GetPage(AmazonItem item)
        {
            int pageNumber = item.Page;
            Pages[pageNumber] = PageState.Loading;
            PageLoading = pageNumber;
            
            Thread thread = new Thread(LinQuerySearch);
            thread.Start(item);
        }


        void GetResultCount(SearchCriteria criteria)
        {
            Thread queryThread = new Thread(LinQueryResultCount);
            queryThread.Start(criteria);
        }


        void LinQuerySearch(object s)
        {
            int pageToGet = item.Page;
            int i = RESULTS_PER_PAGE * (pageToGet - 1);
            int j = RESULTS_PER_PAGE;

            int searchId = CurrentSearch;

            if (CurrentSearch == searchId)
            {
                // PageGet Completed
                if (s.GetType() == typeof(AmazonBook))
                {
                    List<AmazonBook> bookResults = QueryBook.Skip(i).Take(j).ToList<AmazonBook>();
                    Dispatcher.BeginInvoke(() =>
                    {
                        foreach (AmazonBook obj in bookResults)
                        {
                            carousel.DisplayableItems.Add(obj);
                        }
                        updatePagingMonitor();
                    });
                }
                else if (s.GetType() == typeof(AmazonDVD))
                {
                    List<AmazonDVD> dvdResults = QueryDvd.Skip(i).Take(j).ToList<AmazonDVD>();
                    Dispatcher.BeginInvoke(() =>
                    {
                        foreach (AmazonDVD obj in dvdResults)
                        {
                            carousel.DisplayableItems.Add(obj);
                        }
                        updatePagingMonitor();
                    });
                    
                }

                Pages[PageLoading] = PageState.Loaded;
                LastPageGot = PageLoading;
                PageLoading = 0;

                // When first page arrives, SearchCompleted
                if (LastPageGot == 1 && firstPageArrivedEventHandler != null)
                {
                    Dispatcher.BeginInvoke(() =>
                    {
                        firstPageArrivedEventHandler();
                    });
                }
            }
        }


        void LinQueryResultCount(object s)
        {
            int searchId = CurrentSearch;
            int resultCount = 0;
            SearchCriteria countCriteria = s as SearchCriteria;

                if (countCriteria.Item.GetType() == typeof(AmazonBook))
                {
                    AmazonBook b = countCriteria.Item as AmazonBook;
                    AmazonQueryProvider<AmazonBook> provider2 = new AmazonQueryProvider<AmazonBook>(new AmazonBookQueryCriteria(), StatusBar.amazonKey);
                    Query<AmazonBook> queryable2 = new Query<AmazonBook>(provider2);

                    var queryCount = from book in queryable2
                                     where book.Title == b.Title && book.Author == b.Author &&
                                           book.Publisher == b.Publisher &&
                                           book.MaximumPrice == b.MaximumPrice && book.MinimumPrice == b.MinimumPrice &&
                                           book.Keywords == b.Keywords
                                     select book;
                    
                    if (countCriteria.OrderBy == OrderClass.Title)
                    {
                        queryCount = countCriteria.Direction == OrderDirection.Ascending ? queryCount.OrderBy(book => book.Title) : queryCount.OrderByDescending(book => book.Title);
                    }
                    else if (countCriteria.OrderBy == OrderClass.Price)
                    {
                        queryCount = countCriteria.Direction == OrderDirection.Ascending ? queryCount.OrderBy(book => book.Price) : queryCount.OrderByDescending(book => book.Price);
                    }

                    resultCount = queryCount.Count();
                }
                else if (countCriteria.Item.GetType() == typeof(AmazonDVD))
                {
                    AmazonDVD d = countCriteria.Item as AmazonDVD;
                    AmazonQueryProvider<AmazonDVD> provider2 = new AmazonQueryProvider<AmazonDVD>(new AmazonDVDQueryCriteria(), StatusBar.amazonKey);
                    Query<AmazonDVD> queryable2 = new Query<AmazonDVD>(provider2);
                    
                    var queryCount = from dvd in queryable2
                                     where dvd.Title == d.Title && dvd.Actor == d.Actor &&
                                           dvd.Publisher == d.Publisher &&
                                           dvd.Director == d.Director &&
                                           dvd.MaximumPrice == d.MaximumPrice && dvd.MinimumPrice == d.MinimumPrice &&
                                           dvd.Keywords == d.Keywords
                                     select dvd;

                    if (countCriteria.OrderBy == OrderClass.Title)
                    {
                        queryCount = queryCount.OrderBy(dvd => dvd.Title);
                    }
                    else if (countCriteria.OrderBy == OrderClass.Price)
                    {
                        queryCount = countCriteria.Direction == OrderDirection.Ascending ? queryCount.OrderBy(dvd => dvd.Price) : queryCount.OrderByDescending(dvd => dvd.Price);
                    }

                    resultCount = queryCount.Count();
                }

                if (CurrentSearch == searchId)
                {
                    Dispatcher.BeginInvoke(() =>
                    {
                        ResultCount = resultCount;
                        if (Pages[1] == PageState.Loaded)
                            updatePagingMonitor();
                        carousel.TotalResultCount = resultCount;
                    });
                }
            }


        bool GettingPage
        {
            get {
                return PageLoading != 0;
            }
        }

        private void tblInfo_LayoutUpdated(object sender, EventArgs e)
        {
            if (tblInfo.Text == "")
                rctBg.Visibility = Visibility.Collapsed;
            else
                rctBg.Visibility = Visibility.Visible;
        }
    }

    enum PageState
    {
        NotLoaded,
        Loaded,
        Loading
    }
}
